package cn.org.seventh.web.common;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.GenericFilterBean;

/**
 * 有名的filter2bean 不过行为被增强 该类是spring sercrity中用到(以前叫acegi) 由于用了配置文件生成工具
 * 同时生成的配置文件在集中的类路径下 原始的该类不会检查是否加载完全需要的bean 这里做了增强 如果判断没有加载 这里会提前加载
 * 
 * @author <a href="mailto:ji.li@symbio-group.com">Andy</a>
 * @since 2008-6-22 上午02:25:23
 * @see org.springframework.web.filter.DelegatingFilterProxy
 */
public class CustomDelegatingFilterProxy extends GenericFilterBean {

	private String targetBeanName;

	private boolean targetFilterLifecycle = false;

	private Filter delegate;

	/**
	 * Set the name of the target bean in the Spring application context. The
	 * target bean must implement the standard Servlet 2.3 Filter interface.
	 * <p>
	 * By default, the <code>filter-name</code> as specified for the
	 * DelegatingFilterProxy in <code>web.xml</code> will be used.
	 */
	public void setTargetBeanName(String targetBeanName) {
		this.targetBeanName = targetBeanName;
	}

	/**
	 * Return the name of the target bean in the Spring application context.
	 */
	protected String getTargetBeanName() {
		return targetBeanName;
	}

	/**
	 * Set whether to invoke the <code>Filter.init</code> and
	 * <code>Filter.destroy</code> lifecycle methods on the target bean.
	 * <p>
	 * Default is "false"; target beans usually rely on the Spring application
	 * context for managing their lifecycle. Setting this flag to "true" means
	 * that the servlet container will control the lifecycle of the target
	 * Filter, with this proxy delegating the corresponding calls.
	 */
	public void setTargetFilterLifecycle(boolean targetFilterLifecycle) {
		this.targetFilterLifecycle = targetFilterLifecycle;
	}

	/**
	 * Return whether to invoke the <code>Filter.init</code> and
	 * <code>Filter.destroy</code> lifecycle methods on the target bean.
	 */
	protected boolean isTargetFilterLifecycle() {
		return targetFilterLifecycle;
	}

	protected void initFilterBean() throws ServletException {
		// If no target bean name specified, use filter name.
		if (targetBeanName == null) {
			targetBeanName = getFilterName();
		}
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws ServletException, IOException {
		// Lazily initialize the delegate if necessary.
		if (delegate == null) {
			WebApplicationContext wac = SpringContextUtils
					.getWebApplicationContext(request, getFilterConfig()
							.getInitParameter("dispatcherServletName"),
							getServletContext());
			delegate = initDelegate(wac);
		}
		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegate, request, response, filterChain);
	}

	public void destroy() {
		if (delegate != null) {
			destroyDelegate(delegate);
		}
	}

	/**
	 * Initialize the Filter delegate, defined as bean the given Spring
	 * application context.
	 * <p>
	 * Default implementation fetches the bean from the application context and
	 * calls the standard <code>Filter.init</code> method on it, passing in
	 * the FilterConfig of this Filter proxy.
	 * 
	 * @param wac
	 *            the root application context
	 * @return the initialized delegate Filter
	 * @throws ServletException
	 *             if thrown by the Filter
	 * @see #getTargetBeanName()
	 * @see #isTargetFilterLifecycle()
	 * @see #getFilterConfig()
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	protected Filter initDelegate(WebApplicationContext wac)
			throws ServletException {
		Filter delegate = (Filter) wac.getBean(getTargetBeanName(),
				Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}

	/**
	 * Actually invoke the delegate Filter with the given request and response.
	 * 
	 * @param delegate
	 *            the delegate Filter
	 * @param request
	 *            the current HTTP request
	 * @param response
	 *            the current HTTP response
	 * @param filterChain
	 *            the current FilterChain
	 * @throws ServletException
	 *             if thrown by the Filter
	 * @throws IOException
	 *             if thrown by the Filter
	 */
	protected void invokeDelegate(Filter delegate, ServletRequest request,
			ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		delegate.doFilter(request, response, filterChain);
	}

	/**
	 * Destroy the Filter delegate. Default implementation simply calls
	 * <code>Filter.destroy</code> on it.
	 * 
	 * @param delegate
	 *            the Filter delegate (never <code>null</code>)
	 * @see #isTargetFilterLifecycle()
	 * @see javax.servlet.Filter#destroy()
	 */
	protected void destroyDelegate(Filter delegate) {
		if (isTargetFilterLifecycle()) {
			delegate.destroy();
		}
	}
}
